/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <assert.h>
#include <iostream>

#include "audio_coding_module.h"
#include "Channel.h"
#include "tick_util.h"
#include "typedefs.h"
#include "common_types.h"

namespace webrtc {

WebRtc_Word32
Channel::SendData(
        const FrameType       frameType,
        const WebRtc_UWord8   payloadType,
        const WebRtc_UWord32  timeStamp,
        const WebRtc_UWord8*  payloadData,
        const WebRtc_UWord16  payloadSize,
        const RTPFragmentationHeader* fragmentation)
{
    WebRtcRTPHeader rtpInfo;
    WebRtc_Word32   status;
    WebRtc_UWord16  payloadDataSize = payloadSize;

    rtpInfo.header.markerBit = false;
    rtpInfo.header.ssrc = 0;
    rtpInfo.header.sequenceNumber = _seqNo++;
    rtpInfo.header.payloadType = payloadType;
    rtpInfo.header.timestamp = timeStamp;
    if(frameType == kAudioFrameCN)
    {
        rtpInfo.type.Audio.isCNG = true;
    }
    else
    {
        rtpInfo.type.Audio.isCNG = false;
    }
    if(frameType == kFrameEmpty)
    {
        // Skip this frame
        return 0;
    }

    rtpInfo.type.Audio.channel = 1;
    // Treat fragmentation separately
    if(fragmentation != NULL)
    {
        if((fragmentation->fragmentationTimeDiff[1] <= 0x3fff) && // silence for too long send only new data
            (fragmentation->fragmentationVectorSize == 2))
        {
            // only 0x80 if we have multiple blocks
            _payloadData[0] = 0x80 + fragmentation->fragmentationPlType[1];
            WebRtc_UWord32 REDheader =  (((WebRtc_UWord32)fragmentation->fragmentationTimeDiff[1]) << 10) + fragmentation->fragmentationLength[1];
            _payloadData[1] = WebRtc_UWord8((REDheader >> 16) & 0x000000FF);
            _payloadData[2] = WebRtc_UWord8((REDheader >> 8) & 0x000000FF);
            _payloadData[3] = WebRtc_UWord8(REDheader & 0x000000FF);

            _payloadData[4] = fragmentation->fragmentationPlType[0];
            // copy the RED data
            memcpy(_payloadData + 5,
                payloadData + fragmentation->fragmentationOffset[1],
                fragmentation->fragmentationLength[1]);
            // copy the normal data
            memcpy(_payloadData + 5 + fragmentation->fragmentationLength[1],
                payloadData + fragmentation->fragmentationOffset[0],
                fragmentation->fragmentationLength[0]);
            payloadDataSize += 5;
        } else
        {
            // single block (newest one)
            memcpy(_payloadData,
                payloadData + fragmentation->fragmentationOffset[0],
                fragmentation->fragmentationLength[0]);
            payloadDataSize = WebRtc_UWord16(fragmentation->fragmentationLength[0]);
            rtpInfo.header.payloadType = fragmentation->fragmentationPlType[0];
        }
    }
    else
    {
        memcpy(_payloadData, payloadData, payloadDataSize);
        if(_isStereo)
        {
            if(_leftChannel)
            {
                memcpy(&_rtpInfo, &rtpInfo, sizeof(WebRtcRTPHeader));
                _leftChannel = false;
                rtpInfo.type.Audio.channel = 1;
            }
            else
            {
                memcpy(&rtpInfo, &_rtpInfo, sizeof(WebRtcRTPHeader));
                _leftChannel = true;
                rtpInfo.type.Audio.channel = 2;
            }
        }
    }

    _channelCritSect->Enter();
    if(_saveBitStream)
    {
        //fwrite(payloadData, sizeof(WebRtc_UWord8), payloadSize, _bitStreamFile);
    }

    if(!_isStereo)
    {
        CalcStatistics(rtpInfo, payloadSize);
    }
    _lastInTimestamp = timeStamp;
    _totalBytes += payloadDataSize;
    _channelCritSect->Leave();

    if(_useFECTestWithPacketLoss)
    {
        _packetLoss += 1;
        if(_packetLoss == 3)
        {
            _packetLoss = 0;
            return 0;
        }
    }

    status = _receiverACM->IncomingPacket(_payloadData, payloadDataSize,
                                          rtpInfo);

    return status;
}

void
Channel::CalcStatistics(
    WebRtcRTPHeader& rtpInfo,
    WebRtc_UWord16   payloadSize)
{
    int n;
    if((rtpInfo.header.payloadType != _lastPayloadType) &&
        (_lastPayloadType != -1))
    {
        // payload-type is changed.
        // we have to terminate the calculations on the previous payload type
        // we ignore the last packet in that payload type just to make things
        // easier.
        for(n = 0; n < MAX_NUM_PAYLOADS; n++)
        {
            if(_lastPayloadType == _payloadStats[n].payloadType)
            {
                _payloadStats[n].newPacket = true;
                break;
            }
        }
    }
    _lastPayloadType = rtpInfo.header.payloadType;

    bool newPayload = true;
    ACMTestPayloadStats* currentPayloadStr;
    for(n = 0; n < MAX_NUM_PAYLOADS; n++)
    {
        if(rtpInfo.header.payloadType == _payloadStats[n].payloadType)
        {
            newPayload = false;
            currentPayloadStr = &_payloadStats[n];
            break;
        }
    }

    if(!newPayload)
    {
        if(!currentPayloadStr->newPacket)
        {
            WebRtc_UWord32 lastFrameSizeSample = (WebRtc_UWord32)((WebRtc_UWord32)rtpInfo.header.timestamp -
                (WebRtc_UWord32)currentPayloadStr->lastTimestamp);
            assert(lastFrameSizeSample > 0);
            int k = 0;
            while((currentPayloadStr->frameSizeStats[k].frameSizeSample !=
                lastFrameSizeSample) &&
                (currentPayloadStr->frameSizeStats[k].frameSizeSample != 0))
            {
                k++;
            }
            ACMTestFrameSizeStats* currentFrameSizeStats =
                &(currentPayloadStr->frameSizeStats[k]);
            currentFrameSizeStats->frameSizeSample = (WebRtc_Word16)lastFrameSizeSample;

            // increment the number of encoded samples.
            currentFrameSizeStats->totalEncodedSamples +=
                lastFrameSizeSample;
            // increment the number of recveived packets
            currentFrameSizeStats->numPackets++;
            // increment the total number of bytes (this is based on
            // the previous payload we don't know the frame-size of
            // the current payload.
            currentFrameSizeStats->totalPayloadLenByte +=
                currentPayloadStr->lastPayloadLenByte;
            // store the maximum payload-size (this is based on
            // the previous payload we don't know the frame-size of
            // the current payload.
            if(currentFrameSizeStats->maxPayloadLen <
                currentPayloadStr->lastPayloadLenByte)
            {
                currentFrameSizeStats->maxPayloadLen =
                    currentPayloadStr->lastPayloadLenByte;
            }
            // store the current values for the next time
            currentPayloadStr->lastTimestamp = rtpInfo.header.timestamp;
            currentPayloadStr->lastPayloadLenByte = payloadSize;
        }
        else
        {
            currentPayloadStr->newPacket          = false;
            currentPayloadStr->lastPayloadLenByte = payloadSize;
            currentPayloadStr->lastTimestamp      = rtpInfo.header.timestamp;
            currentPayloadStr->payloadType        = rtpInfo.header.payloadType;
        }
    }
    else
    {
        n = 0;
        while(_payloadStats[n].payloadType != -1)
        {
            n++;
        }
        // first packet
        _payloadStats[n].newPacket          = false;
        _payloadStats[n].lastPayloadLenByte = payloadSize;
        _payloadStats[n].lastTimestamp      = rtpInfo.header.timestamp;
        _payloadStats[n].payloadType        = rtpInfo.header.payloadType;
    }
}

Channel::Channel(WebRtc_Word16 chID) :
_receiverACM(NULL),
_seqNo(0),
_channelCritSect(CriticalSectionWrapper::CreateCriticalSection()),
_bitStreamFile(NULL),
_saveBitStream(false),
_lastPayloadType(-1),
_isStereo(false),
_leftChannel(true),
_lastInTimestamp(0),
_packetLoss(0),
_useFECTestWithPacketLoss(false),
_beginTime(TickTime::MillisecondTimestamp()),
_totalBytes(0)
{
    int n;
    int k;
    for(n = 0; n < MAX_NUM_PAYLOADS; n++)
    {
        _payloadStats[n].payloadType = -1;
        _payloadStats[n].newPacket   = true;
        for(k = 0; k < MAX_NUM_FRAMESIZES; k++)
        {
            _payloadStats[n].frameSizeStats[k].frameSizeSample = 0;
            _payloadStats[n].frameSizeStats[k].maxPayloadLen   = 0;
            _payloadStats[n].frameSizeStats[k].numPackets      = 0;
            _payloadStats[n].frameSizeStats[k].totalPayloadLenByte = 0;
            _payloadStats[n].frameSizeStats[k].totalEncodedSamples = 0;
        }
    }
    if(chID >= 0)
    {
        _saveBitStream = true;
        char bitStreamFileName[500];
        sprintf(bitStreamFileName, "bitStream_%d.dat", chID);
        _bitStreamFile = fopen(bitStreamFileName, "wb");
    }
    else
    {
        _saveBitStream = false;
    }
}

Channel::~Channel()
{
    delete _channelCritSect;
}

void
Channel::RegisterReceiverACM(AudioCodingModule* acm)
{
    _receiverACM = acm;
    return;
}

void
Channel::ResetStats()
{
    int n;
    int k;
    _channelCritSect->Enter();
    _lastPayloadType = -1;
    for(n = 0; n < MAX_NUM_PAYLOADS; n++)
    {
        _payloadStats[n].payloadType = -1;
        _payloadStats[n].newPacket   = true;
        for(k = 0; k < MAX_NUM_FRAMESIZES; k++)
        {
            _payloadStats[n].frameSizeStats[k].frameSizeSample = 0;
            _payloadStats[n].frameSizeStats[k].maxPayloadLen   = 0;
            _payloadStats[n].frameSizeStats[k].numPackets      = 0;
            _payloadStats[n].frameSizeStats[k].totalPayloadLenByte = 0;
            _payloadStats[n].frameSizeStats[k].totalEncodedSamples = 0;
        }
    }
    _beginTime = TickTime::MillisecondTimestamp();
    _totalBytes = 0;
    _channelCritSect->Leave();
}

WebRtc_Word16
Channel::Stats(CodecInst& codecInst, ACMTestPayloadStats& payloadStats)
{
    _channelCritSect->Enter();
    int n;
    payloadStats.payloadType = -1;
    for(n = 0; n < MAX_NUM_PAYLOADS; n++)
    {
        if(_payloadStats[n].payloadType == codecInst.pltype)
        {
            memcpy(&payloadStats, &_payloadStats[n], sizeof(ACMTestPayloadStats));
            break;
        }
    }
    if(payloadStats.payloadType == -1)
    {
        _channelCritSect->Leave();
        return -1;
    }
    for(n = 0; n < MAX_NUM_FRAMESIZES; n++)
    {
        if(payloadStats.frameSizeStats[n].frameSizeSample == 0)
        {
            _channelCritSect->Leave();
            return 0;
        }
        payloadStats.frameSizeStats[n].usageLenSec =
            (double)payloadStats.frameSizeStats[n].totalEncodedSamples
            / (double)codecInst.plfreq;

        payloadStats.frameSizeStats[n].rateBitPerSec =
            payloadStats.frameSizeStats[n].totalPayloadLenByte * 8 /
            payloadStats.frameSizeStats[n].usageLenSec;

    }
    _channelCritSect->Leave();
    return 0;
}

void
Channel::Stats(WebRtc_UWord32* numPackets)
{
    _channelCritSect->Enter();
    int k;
    int n;
    memset(numPackets, 0, MAX_NUM_PAYLOADS * sizeof(WebRtc_UWord32));
    for(k = 0; k < MAX_NUM_PAYLOADS; k++)
    {
        if(_payloadStats[k].payloadType == -1)
        {
            break;
        }
        numPackets[k] = 0;
        for(n = 0; n < MAX_NUM_FRAMESIZES; n++)
        {
            if(_payloadStats[k].frameSizeStats[n].frameSizeSample == 0)
            {
                break;
            }
            numPackets[k] +=
                _payloadStats[k].frameSizeStats[n].numPackets;
        }
    }
    _channelCritSect->Leave();
}

void
Channel::Stats(WebRtc_UWord8* payloadType, WebRtc_UWord32* payloadLenByte)
{
    _channelCritSect->Enter();

    int k;
    int n;
    memset(payloadLenByte, 0, MAX_NUM_PAYLOADS * sizeof(WebRtc_UWord32));
    for(k = 0; k < MAX_NUM_PAYLOADS; k++)
    {
        if(_payloadStats[k].payloadType == -1)
        {
            break;
        }
        payloadType[k] = (WebRtc_UWord8)_payloadStats[k].payloadType;
        payloadLenByte[k] = 0;
        for(n = 0; n < MAX_NUM_FRAMESIZES; n++)
        {
            if(_payloadStats[k].frameSizeStats[n].frameSizeSample == 0)
            {
                break;
            }
            payloadLenByte[k] += (WebRtc_UWord16)
                _payloadStats[k].frameSizeStats[n].totalPayloadLenByte;
        }
    }

    _channelCritSect->Leave();
}


void
Channel::PrintStats(CodecInst& codecInst)
{
    ACMTestPayloadStats payloadStats;
    Stats(codecInst, payloadStats);
    printf("%s %d kHz\n",
        codecInst.plname,
        codecInst.plfreq / 1000);
    printf("=====================================================\n");
    if(payloadStats.payloadType == -1)
    {
        printf("No Packets are sent with payload-type %d (%s)\n\n",
            codecInst.pltype,
            codecInst.plname);
        return;
    }
    for(int k = 0; k < MAX_NUM_FRAMESIZES; k++)
    {
        if(payloadStats.frameSizeStats[k].frameSizeSample == 0)
        {
            break;
        }
        printf("Frame-size.................... %d samples\n",
            payloadStats.frameSizeStats[k].frameSizeSample);
        printf("Average Rate.................. %.0f bits/sec\n",
            payloadStats.frameSizeStats[k].rateBitPerSec);
        printf("Maximum Payload-Size.......... %d Bytes\n",
            payloadStats.frameSizeStats[k].maxPayloadLen);
        printf("Maximum Instantaneous Rate.... %.0f bits/sec\n",
            ((double)payloadStats.frameSizeStats[k].maxPayloadLen * 8.0 *
            (double)codecInst.plfreq) /
            (double)payloadStats.frameSizeStats[k].frameSizeSample);
        printf("Number of Packets............. %u\n",
               (unsigned int)payloadStats.frameSizeStats[k].numPackets);
        printf("Duration...................... %0.3f sec\n\n",
            payloadStats.frameSizeStats[k].usageLenSec);

    }

}

WebRtc_UWord32
Channel::LastInTimestamp()
{
    WebRtc_UWord32 timestamp;
    _channelCritSect->Enter();
    timestamp = _lastInTimestamp;
    _channelCritSect->Leave();
    return timestamp;
}

double
Channel::BitRate()
{
    double rate;
    WebRtc_UWord64 currTime = TickTime::MillisecondTimestamp();
    _channelCritSect->Enter();
    rate =   ((double)_totalBytes * 8.0)/ (double)(currTime - _beginTime);
    _channelCritSect->Leave();
    return rate;
}

} // namespace webrtc
